﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Core.RabbitMQBus.Common;
using Core.RabbitMQBus.IocHelper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;

namespace Core.RabbitMQBus.EventBus
{
    /// <summary>
    /// 订阅者注册缓存
    /// </summary>
    public static class RegisterSubscriberCache
    {
        static ConcurrentDictionary<string, SubscriberServiceMethodInfo> SubscriberServiceMethodDictionary = new ConcurrentDictionary<string, SubscriberServiceMethodInfo>();

        /// <summary>
        /// 注册订阅
        /// </summary>
        /// <param name="service">注入容器</param>
        /// <param name="assemblyName">订阅者的程序集名称</param>
        /// <param name="subscribeClassEndsWithName">订阅者类的后缀名称</param>
        public static void RabbitMQRegisterSubscriber(this IServiceCollection service, string assemblyName, string subscribeClassEndsWithName)
        {
            if (string.IsNullOrEmpty(assemblyName))
                throw new ArgumentNullException(nameof(assemblyName));
            var assembly = RuntimeHelper.GetAssembly(assemblyName);
            if (assembly == null)
            {
                throw new DllNotFoundException($"the dll \"{assemblyName}\" not be found");
            }
            //获取程序集中实现了ISubscribe接口的类型
            var subscribeTypes = assembly.GetTypes().Where(t => t.GetTypeInfo().IsClass && typeof(ISubscribe).IsAssignableFrom(t.GetTypeInfo()));
            var serviceProvider = service.BuildServiceProvider();
            var rabbitMqSubscriber = serviceProvider.GetService<IRabbitMqSubscriber>();
            var subscribeTypeList = subscribeTypes.Where(s => s.GetMethods().Where(e => e.GetCustomAttribute(typeof(SubscribeAttribute)) != null).Any()).ToList();
            foreach (var type in subscribeTypeList)
            {
                var subscribeType = type.GetInterfaces().FirstOrDefault(e => e.Name.EndsWith(subscribeClassEndsWithName));
                var subscribeService = serviceProvider.GetService(subscribeType);
                var methodList = type.GetMethods().Where(e => e.GetCustomAttribute(typeof(SubscribeAttribute)) != null).ToList();
                foreach (var method in methodList)
                {
                    SubscriberServiceMethodInfo subscriberServiceMethod = new SubscriberServiceMethodInfo();
                    subscriberServiceMethod.SubscriberService = subscribeService;
                    var subscribeAttribute = method.GetCustomAttribute(typeof(SubscribeAttribute)) as SubscribeAttribute;
                    if (subscribeAttribute != null)
                    {
                        var methodParameters = method.GetParameters().FirstOrDefault();
                        var parameterType = methodParameters?.ParameterType;
                        var queueName = subscribeAttribute.QueueName;
                        subscriberServiceMethod.MethodInfo = method;
                        subscriberServiceMethod.MethodParameterType = parameterType;
                        SubscriberServiceMethodDictionary.GetOrAdd(queueName, subscriberServiceMethod);
                        rabbitMqSubscriber.Subscriber(queueName);
                    }
                }
            }
        }

        public static void RabbitMQControllerRegisterSubscriber(this IServiceCollection service, string assemblyName)
        {
            if (string.IsNullOrEmpty(assemblyName))
                throw new ArgumentNullException(nameof(assemblyName));
            var assembly = RuntimeHelper.GetAssembly(assemblyName);
            if (assembly == null)
            {
                throw new DllNotFoundException($"the dll \"{assemblyName}\" not be found");
            }
            var assemblys = assembly.GetTypes().AsEnumerable()
               .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToList();
            var serviceProvider = service.BuildServiceProvider();
            var rabbitMqSubscriber = serviceProvider.GetService<IRabbitMqSubscriber>();
            assemblys.ForEach(type =>
            {
                var subscribeService = serviceProvider.GetService(type);
                var methodList = type.GetMethods().Where(e => e.GetCustomAttribute(typeof(SubscribeAttribute)) != null).ToList();
                foreach (var method in methodList)
                {
                    SubscriberServiceMethodInfo subscriberServiceMethod = new SubscriberServiceMethodInfo();
                    subscriberServiceMethod.SubscriberService = subscribeService;
                    if (method.GetCustomAttribute(typeof(SubscribeAttribute)) is SubscribeAttribute subscribeAttribute)
                    {
                        var methodParameters = method.GetParameters().FirstOrDefault();
                        var parameterType = methodParameters?.ParameterType;
                        var queueName = subscribeAttribute.QueueName;
                        subscriberServiceMethod.MethodInfo = method;
                        subscriberServiceMethod.MethodParameterType = parameterType;
                        SubscriberServiceMethodDictionary.GetOrAdd(queueName, subscriberServiceMethod);
                        rabbitMqSubscriber.Subscriber(queueName);
                    }
                }
            });
        }


        /// <summary>
        /// 根据队列名称获取订阅者的方法
        /// </summary>
        /// <param name="queueName"></param>
        /// <returns></returns>
        public static SubscriberServiceMethodInfo GetSubscriberMethod(string queueName)
        {
            SubscriberServiceMethodDictionary.TryGetValue(queueName, out SubscriberServiceMethodInfo subscriberServiceMethod);
            return subscriberServiceMethod;
        }
    }

    public class SubscriberServiceMethodInfo
    {
        /// <summary>
        /// 方法
        /// </summary>
        public MethodInfo MethodInfo { get; set; }

        /// <summary>
        /// 订阅者Service
        /// </summary>
        public object SubscriberService { get; set; }

        /// <summary>
        /// 参数类型
        /// </summary>
        public Type MethodParameterType { get; set; }
    }
}
